
package dovs.phases;

import dovs.*;
import dovs.analysis.*;
import dovs.node.*;
import static dovs.Util.*;

import java.util.*;

/**
 * 	Compiler phase to link the uses of all types
 *	to their definitions.
 */
public aspect TypeLinking extends DepthFirstAdapter {
	/** The declaration of this named type */
	public PTypeDecl ANamedType.decl;
	/** The type associated with this class declaration */
	public ANamedType PTypeDecl.type;

	@Override 
	public void inAClassTypeDecl(AClassTypeDecl classd) {
		classd.type = makeType(classd.canonical_name, false);
		classd.type.decl = classd;
	}

	
	@Override 
	public void inANamedType(ANamedType type) {
		type.decl = linkType(type);
		if (type.decl == null) {
			errorUnresolvedType(type);
		}
	}

	public static PTypeDecl linkType(ANamedType type) {
		switch (type.getName().kindPName()) {
		case QUALIFIED: // Fully qualified name already
			return Environments.lookupNamedType(type.getName().nameText());
		case SIMPLE:
			TIdentifier id = ((ASimpleName)type.getName()).getIdentifier();
			return linkTypeFromIdentifier(id);
		default:
			throw new InternalCompilerError("Unknown name kind"); 
		}
	}

	/**
	 * Find the declaration of the named type denoted by the given identifier
	 * according to the rules in Section 6.5.5.1 of the JLS.
	 * 
	 * @param id
	 *            the identifier to look up. Its scope is specified by its
	 *            placement in the AST.
	 * @return the corresponding type declaration, or <code>null</code> if the
	 *         identifier could not be resolved as a type.
	 */
	public static PTypeDecl linkTypeFromIdentifier(TIdentifier id) {
		// TODO
		return null;
	}


	
	// /////////////////////////////////////////////////////////////////////////
	// ERROR MESSAGES
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Reports the error that a simple name type doesn't resolve unique to a
	 * type.
	 * 
	 * @param node
	 *            the identifier holding the name of the ambiguous class
	 */
	@SuppressWarnings("unused")
	private static void errorAmbiguousClassName(TIdentifier node) {
		Errors.error(ErrorType.AMBIGUOUS_CLASS_NAME, node, 
				"Ambiguous class name " + node.getText(), false);
	}
	
	/**
	 * Reports the error that a non-existing package is refered in an
	 * import-on-demand declaration.
	 * 
	 * @param node
	 *            the import-on-demand declaration
	 */
	@SuppressWarnings("unused")
	private static void errorNonExistingPackage(AOnDemandImportDecl node) {
		Errors.error(ErrorType.NON_EXISTING_PACKAGE,node.getName().getToken(), 
				"Package " + node.getName().nameText() + " does not exist", 
				false);
	}
	
	/**
	 * Reports the error that a prefix of a package name clashes with the name
	 * of a type.
	 * 
	 * @param name
	 *            the package name
	 * @param prefix
	 *            the clashing prefix
	 */
    @SuppressWarnings("unused") 
    private static void errorPackageClashWithType(PName name, 
    		String prefix) {
    	Errors.error(ErrorType.PACKAGE_CLASH_WITH_TYPE,
    			name.getToken(), 
    			"Prefix " + prefix + " of package " + 
    			name.nameText() + 
    			" clashes with type of the same name.", false);
    }
	
	/**
	 * Reports the error that a prefix of a type name resolves to a type.
	 * 
	 * @param node
	 *            the full type name
	 * @param prefix
	 *            the prefix which resolves to a type
	 */
	@SuppressWarnings("unused")
	private static void errorPrefixResolvesToType(AQualifiedName node, 
			String prefix) {
		Errors.error(ErrorType.PREFIX_RESOLVES_TO_TYPE, node.getToken(), 
				"Prefix " + prefix + " of type name " + node.nameText() + 
				" is itself a valid type", false);
	}
	
	/**
	 * Reports the error that the name of declared class (or interface) clashes
	 * with a single-type-import declaration.
	 * 
	 * @param node
	 *            the type declaration
	 * @param importName
	 *            the name of the imported type
	 */
	@SuppressWarnings("unused")
	private static void errorSingleTypeImportClashWithClass(PTypeDecl node, 
			String importName) {
		Errors.error(ErrorType.SINGLE_TYPE_IMPORT_CLASH_WITH_CLASS, 
				node.getName(), "Name clash with imported type "+ importName, 
				false);
	}
	
	/**
	 * Reports the error that two single-type-imports clash.
	 * 
	 * @param node
	 *            one of the single-type-import declarations
	 * @param otherImportName
	 *            the name of the other imported type
	 */
	@SuppressWarnings("unused")
	private static void errorTwoSingleTypeImportsClash(ASingleImportDecl node, 
			String otherImportName) {
		Errors.error(ErrorType.TWO_SINGLE_TYPE_IMPORTS_CLASH, 
				node.getName().getToken(), "Name clash between " + 
				node.getName().nameText() + "." + node.getIdentifier().getText() + 
				" and " + otherImportName, false);
	}
	
	/**
	 * Reports the error that a named type doesn't resolve to a type defined in
	 * the program or in the class library.
	 * 
	 * @param node
	 *            the named type
	 */
	@SuppressWarnings("unused")
	private static void errorUnresolvedType(ANamedType node) {
		Errors.error(ErrorType.UNRESOLVED_TYPE, node.getName().getToken(), 
				"Could not find type "+node.getName().nameText(), false);
	}
	
	/**
	 * Reports the error that the named type of a single-type-import doesn't
	 * resolve to a type in the program or in the class library.
	 * 
	 * @param single_type_import
	 *            the single-type-import declaration
	 */
	@SuppressWarnings("unused")
	private static void errorUnresolvedType(ASingleImportDecl single_type_import) {
		String type_name = single_type_import.getName().nameText() + "." + 
			single_type_import.getIdentifier().getText();
		Errors.error(ErrorType.UNRESOLVED_TYPE, 
				single_type_import.getIdentifier(), 
				"Could not find type " + type_name, false);
	}

}
